﻿using System;
using System.Data.Objects;
using System.Linq;
using System.Web.Mvc;
using Citi.Scheduler.Domain;
using Citi.Scheduler.Domain.Utils;
using Citi.Scheduler.WebUi.Models;
using System.Collections.Generic;
using Citi.Scheduler.WebUi.Helpers;

namespace Citi.Scheduler.WebUi.Controllers
{
    public class RoutineExecutionController : ControllerBase<RoutineExecutionModel, RoutineExecution>
    {
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public ActionResult Dashboard()
        {
            var models = GetRoutineExecutions();

            return View(models);
        }

        private IEnumerable<RoutineExecutionModel> GetRoutineExecutions(DateTime? startDate = null, DateTime? endDate = null)
        {
            ViewBag.ShowOptions = true;

            Context.Instance.RoutineExecutions.MergeOption = MergeOption.OverwriteChanges;
            Context.Instance.RoutineExecutionHistories.MergeOption = MergeOption.OverwriteChanges;

            var executions = new RoutineExecution().SelectAllActive<RoutineExecution>()
                .Where(x => (!startDate.HasValue || !endDate.HasValue) || (x.ScheduledDateTime >= startDate && x.ScheduledDateTime <= endDate))
                .OrderByDescending(x => x.ScheduledDateTime).ToList();
            //executions.ForEach(x => Context.Instance.LoadProperty(x, execution => execution.RoutineExecutionHistories));

            var models = executions.Select(x => new RoutineExecutionModel().CreateModel(x));
            return models;
        }

        public ActionResult DashboardFilter(DashboardFilterModel model)
        {
            var models = GetRoutineExecutions(model.DateStart, model.DateEnd);

            models = models.Where(x => x.MachineId == model.MachineId || model.MachineId == 0);
            models = models.Where(x => x.Routine.CategoryId == model.CategoryId || model.CategoryId == 0);
            models = models.Where(x => x.Routine.ResponsibleId == model.ResponsibleId || model.ResponsibleId == 0);
            models = models.Where(x => x.RoutineId == model.RoutineId || model.RoutineId == 0);
            models = models.Where(x => (x.RoutineExecutionHistories.Any() && (int)x.RoutineExecutionHistories.Last().Status == model.Status) || model.Status == 0);
            models = models.Where(x => x.ScheduledDateTime >= model.DateStart || model.DateStart == null);
            models = models.Where(x => x.ScheduledDateTime <= model.DateEnd || model.DateEnd == null);

            return PartialView(models);
        }

        public ActionResult RealTime(int routineExecutionId)
        {
            var execution = new RoutineExecution().SelectById<RoutineExecution>(routineExecutionId);
            return View(execution);
        }

        public ActionResult EmailDashboard(int emailScheduleId)
        {
            var emailSchedule = new EmailSchedule().SelectById<EmailSchedule>(emailScheduleId);

            ViewBag.UserName = emailSchedule.User.Name;
            ViewBag.CategoryName = emailSchedule.Category.Name;

            var routines = new RoutineExecution().GetAllChildrenRoutinesByDate(emailSchedule.CategoryId,
                DateTime.Today.AddDays(-emailSchedule.DaysToReport), DateTime.Today);

            return View(new RoutineExecutionModel().CreateModel(routines.OrderBy(x => x.ScheduledDateTime).ThenBy(x => x.EndsOn)));
        }

        [ValidateInput(false)]
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult PeriodSummary(DateTime start, DateTime end, int routineId = 0, int categoryId = 0)
        {
            var executions = (routineId != 0
                                 ? new RoutineExecution().GetByRoutineId(routineId)
                                 : new RoutineExecution().GetAllChildrenRoutinesByDate(categoryId, start, end))
                                                         .Where(x => x.ScheduledDateTime.Date >= start && x.ScheduledDateTime.Date <= end)
                                                         .OrderBy(x => x.StartsOn).ToList();

            if (!executions.Any())
                return Json(new { html = string.Empty, noData = true }, JsonRequestBehavior.AllowGet);

            var html = GetSummaryInformation(start, end, categoryId, executions);

            var runtime = ChartsHelper.GetRuntimeExecutionChart(executions);
            var errors = ChartsHelper.GetErrorDataChart(executions);

            return Json(new
                {
                    html,
                    noData = false,
                    series = runtime.Series,
                    categories = runtime.Categories,
                    errorsSeries = errors.Series,
                    errorsCategories = errors.Categories,
                    period = string.Format("Information: {0} to {1}", start.ToString("dd/MM/yyyy"), end.ToString("dd/MM/yyyy"))
                }, JsonRequestBehavior.AllowGet);
        }

        private string GetSummaryInformation(DateTime start, DateTime end, int categoryId, IEnumerable<RoutineExecution> executions)
        {
            var executionsExecuted = executions.Where(x => x.EndsOn.HasValue && x.StartsOn.HasValue).OrderBy(x => x.ScheduledDateTime).ToList();

            string html = string.Empty;

            if (categoryId != 0) return html;

            var periodSummary = new PeriodSummaryModel
            {
                Start = start,
                End = end,
                AverageExecution = executionsExecuted.Average(x => (x.EndsOn.Value - x.StartsOn.Value).TotalMinutes),
                MaximumExecution = executionsExecuted.Max(x => (x.EndsOn.Value - x.StartsOn.Value).TotalMinutes),
                MinimumExecution = executionsExecuted.Min(x => (x.EndsOn.Value - x.StartsOn.Value).TotalMinutes),
                ErrorsCount = executionsExecuted.Count(x => x.RoutineExecutionHistories
                    .Any(y => y.Status == (short)RoutineExecutionStatus.Error
                              || y.Status == (short)RoutineExecutionStatus.ExecutedWithError)),
                ExecutionsCount = executionsExecuted.Count(),
                Growth = CalculePercentageBetween(executionsExecuted.First(), executionsExecuted.Last()),
                PercentageVariation = executionsExecuted.First().Routine.PercentageAcceptableVariation,
                ExecutionTime = executionsExecuted.First().Routine.EstimatedTime
            };

            if (executionsExecuted.Count() > 1)
            {
                var greaterVariation = executionsExecuted.Take(executionsExecuted.Count() - 1)
                    .Max(x => (x.EndsOn.Value - x.StartsOn.Value) -
                              (executionsExecuted[executionsExecuted.IndexOf(x) + 1].EndsOn.Value -
                               executionsExecuted[executionsExecuted.IndexOf(x) + 1].StartsOn.Value));

                var greaterVariationStart = executionsExecuted.Take(executionsExecuted.Count() - 1)
                    .First(x => (x.EndsOn.Value - x.StartsOn.Value) -
                                (executionsExecuted[executionsExecuted.IndexOf(x) + 1].EndsOn.Value -
                                 executionsExecuted[executionsExecuted.IndexOf(x) + 1].StartsOn.Value) ==
                                greaterVariation);

                var greaterVariationEnd = executionsExecuted[executionsExecuted.IndexOf(greaterVariationStart) + 1];

                periodSummary.GreaterVariationStart = greaterVariationStart.StartsOn.Value.Date;
                periodSummary.GreaterVariationEnd = greaterVariationEnd.StartsOn.Value.Date;
                periodSummary.GreaterVariation = CalculePercentageBetween(greaterVariationStart, greaterVariationEnd);
            }

            html = GetControlData("~/Views/RoutineExecution/PeriodSummary.cshtml", periodSummary);

            return html;
        }

        private double CalculePercentageBetween(RoutineExecution first, RoutineExecution last)
        {
            var lastTime = (last.EndsOn.Value - last.StartsOn.Value).TotalMinutes;
            var firstTime = (first.EndsOn.Value - first.StartsOn.Value).TotalMinutes;

            return lastTime - firstTime * 100 / firstTime;
        }

        [NoCache]
        public ActionResult LastErrors(DailyExecutionFilterModel model)
        {
            //var routines = new RoutineExecution().SelectAllActive<RoutineExecution>().Where(x => x.Error);
            var routines = new RoutineExecution().GetRoutineDashboard(model.StartDate, model.EndDate).Where(x => x.Status == (int)RoutineExecutionStatus.Error);
            return GetTopExecution(routines, model);
        }

        [NoCache]
        public ActionResult LastExecutedWithoutError(DailyExecutionFilterModel model)
        {
            //var routines = new RoutineExecution().SelectAllActive<RoutineExecution>().Where(x => x.ExecutedWithoutError);
            var routines = new RoutineExecution().GetRoutineDashboard(model.StartDate, model.EndDate).Where(x => x.Status == (int)RoutineExecutionStatus.Executed);
            return GetTopExecution(routines, model);
        }

        [NoCache]
        public ActionResult LastExecutions(DailyExecutionFilterModel model)
        {
            //var routines = new RoutineExecution().SelectAllActive<RoutineExecution>().Where(x => x.FinishedExecution);
            var routines = new RoutineExecution().GetRoutineDashboard(model.StartDate, model.EndDate).
                Where(x => x.Status == (int)RoutineExecutionStatus.Executed
                || x.Status == (int)RoutineExecutionStatus.Error
                || x.Status == (int)RoutineExecutionStatus.Stopped);

            return GetTopExecution(routines, model);
        }

        [NoCache]
        public ActionResult InProgressExecutions(DailyExecutionFilterModel model)
        {
            //var routines = new RoutineExecution().SelectAllActive<RoutineExecution>().Where(x => x.InProgress);
            var routines = new RoutineExecution().GetRoutineDashboard(model.StartDate, model.EndDate).Where(x => x.Status == (int)RoutineExecutionStatus.InProgress);

            return GetTopExecution(routines, model);
        }

        [NoCache]
        public ActionResult NextExecutions(DailyExecutionFilterModel model)
        {
            //var routines = new RoutineExecution().SelectAllActive<RoutineExecution>().Where(x => !x.StartsOn.HasValue && x.LastHistory != null && !x.Canceled);
            var routines = new RoutineExecution().GetRoutineDashboard(model.StartDate, model.EndDate).Where(x => !x.StartsOn.HasValue
                && (x.Status == (int)RoutineExecutionStatus.PredecessorNotScheduled
                || x.Status == (int)RoutineExecutionStatus.PredecessorRoutineWithError
                || x.Status == (int)RoutineExecutionStatus.Scheduled
                || x.Status == (int)RoutineExecutionStatus.WaitingFreeMachine
                || x.Status == (int)RoutineExecutionStatus.WaitingPredecessorRoutine
                || x.Status == (int)RoutineExecutionStatus.WaitingSpecificFreeMachine));

            return GetTopExecution(routines, model);
        }

        [NoCache]
        private ActionResult GetTopExecution(IEnumerable<RoutineDashboard> dashboard, DailyExecutionFilterModel model)
        {
            ViewBag.TopExecutions = true;

            var models = new List<RoutineExecutionModel>();

            foreach (var routine in dashboard)
            {
                models.Add(new RoutineExecutionModel
                       {
                           Routine = new RoutineModel()
                           {
                               Id = routine.RoutineId,
                               Name = routine.Routine,
                               User = new UserModel { Name = routine.UserName },
                               EstimatedTime = routine.EstimatedTime,
                               PercentageAcceptableVariation = routine.PercentageAcceptableVariation,
                               CustomerSLA = routine.CustomerSLA.Value,
                               ResponsibleId = routine.ResponsibleId,
                               CategoryId = routine.CategoryId,
                               LibraryVersion = new LibraryVersionModel { Type = (LibraryType)routine.LibraryVersionType }
                           },
                           RoutineId = routine.RoutineId,
                           CustomerSLA = routine.CustomerSLA,
                           ScheduledDateTime = routine.ScheduledDateTime,
                           EndsOn = routine.EndsOn,
                           StartsOn = routine.StartsOn,
                           Id = routine.Id,
                           MachineId = routine.MachineId,
                           RoutineExecutionHistories =
                           new List<RoutineExecutionHistoryModel>() { new RoutineExecutionHistoryModel { Status = (RoutineExecutionStatus)routine.Status } }
                       });
                //});
            }

            var categoriesId = new List<int>();

            if (model.CategoryId > 0 && model.IncludeCategoryChildren)
            {
                var category = new Category().SelectById<Category>(model.CategoryId);
                categoriesId = category.GetAllChildren().Select(x => x.Id).ToList();
            }
            else
                categoriesId.Add(model.CategoryId);

            models = models.Where(
                x => x.ScheduledDateTime.Date <= model.EndDate && x.ScheduledDateTime.Date >= model.StartDate
                     && (model.MachineId == 0 || x.MachineId == model.MachineId)
                     && (model.ResponsibleId == 0 || x.Routine.ResponsibleId == model.ResponsibleId)
                     && (model.CategoryId == 0 || categoriesId.Contains(x.Routine.CategoryId))
                     && (string.IsNullOrWhiteSpace(model.RoutineName) || x.Routine.Name.ToLower().Contains(model.RoutineName.ToLower()))
                     && x.Routine.LibraryVersion.Type == model.LibraryType).ToList();

            models.GroupBy(c => new { c.RoutineId, c.ScheduledDateTime.Date })
                .Select(g => g.OrderByDescending(c => c.EndsOn).First())
                .Select(x => x).Take(model.ExecutionToShow).OrderByDescending(x => x.EndsOn).ThenBy(x => x.ScheduledDateTime);

            return PartialView("ListExecution", models);
        }

        public ActionResult DailyExecutionFilter(LibraryType libraryType)
        {
            var model = new DailyExecutionFilterModel
            {
                ExecutionToShow = 10000,
                EndDate = DateTime.Today,
                StartDate = DateTime.Today,
                LibraryType = libraryType
            };

            FillViewBags();
            return PartialView(model);
        }

        public ActionResult StatusExecution(DailyExecutionFilterModel model)
        {
            //model.ExecutionToShow = 10000;
            //model.EndDate = DateTime.Today;
            //model.StartDate = DateTime.Today;

            return PartialView(model);
        }

        public ActionResult TodayExecutions(LibraryType libraryType)
        {
            ViewBag.LibraryType = libraryType;
            return View();
        }

        public ActionResult Index(LibraryType libraryType)
        {
            ViewBag.LibraryType = libraryType;
            return View();
        }

        public void FillViewBags(int categoryId = 0, int machineId = 0, int responsibleId = 0)
        {
            ViewBag.Categories = new Category().SelectAll<Category>().OrderBy(x => x.Name).ToSelectList(x => x.Name, x => x.Id.ToString(), "All", categoryId.ToString());
            ViewBag.Machines = new Machine().SelectAll<Machine>().OrderBy(x => x.HostName).ToSelectList(x => x.HostName, x => x.Id.ToString(), "All", machineId.ToString());
            ViewBag.Responsibles = new User().SelectAll<User>().OrderBy(x => x.Name).ToSelectList(x => x.Name, x => x.Id.ToString(), "All", responsibleId.ToString());
        }

        public ActionResult Cancel(int id)
        {
            RoutineExecutionHistory.InsertHistory(id, RoutineExecutionStatus.Canceled, HistoryDetail.CanceledByUser);

            return RedirectToAction("Dashboard");
        }
    }
}
